Kandria Combat System
This document describes the internals of how the combat and animation systems function. Kandria uses a combination of information provided natively by Aseprite, as well as an additional data file to describe extra per-animation and per-frame properties that the combat system needs to function.
For this reason, animation data is split across the following files:
x.ase
Aseprite source file. This is only useful during development.x.json
Aseprite exported JSON data. When exporting the animation from Aseprite, this file will be generated automatically. See exporting from asepritex.png
Aseprite exported sprite atlas. This is the packed atlas and contains all frames in a tight and efficient single image.x.lisp
External animation data. This file is generated by the in-game animation editor and contains extra data relevant for combat. See combat data format.
For the most part you will be working within Aseprite and Kandria's editor. You should not have to modify the files manually often.
Combat Mechanics
Any character in the game can be in several internal states, though for the purpose of this system, we'll focus on "animated" and "normal" states. In the normal state, animation is controlled tightly by the game and changes in actions and animations can occur at any point. Animations that are used in the normal state don't need any combat data, only basic animation data.
Every animation regardless of state has two properties:
loop-to
When the animation reaches its end, it will loop back to this frame index. The index is zero-based. If no loop-to index is set, it instead usesnext
This designates which animation should be started if the current animation reaches its end and does not have a loop.
For animations that are used in the animated state, the movement of the character is no longer in the player's hands but is instead governed by per-frame motion data. The per-frame data contains the following attributes:
velocity
The added velocity of the character at this frame.multiplier
The velocity multiplier at this frame. This is useful to gradually slow down or accelerate existing character velocity. Typically this is set to zero to make thevelocity
parameter be the effective velocity of the character.hurtbox
The box relative to the character's position that designates where the attack would hit if colliding with another character.knockback
The velocity that's added to a character at this frame if that character collides with the hurtbox.damage
The amount of damage that's dealt to the character if the character collides with the hurtbox.stun-time
The amount of time in seconds for which the other character is stunned for when colliding with the hurtbox.flags
interruptable
When set the animation can be interrupted by a collision with a hurtbox.invincible
When set the character cannot be damaged by a collision with a hurtbox.cancelable
When set the player can cancel the animation and instead switch to a different animation or state.
effect
The name of an effect to trigger on this frame. Effects can cause sounds or particle effects.
The animated state is used for attacks, getting hit, and dying.
In the player's case, animations can be buffered, meaning that the player can hit the button to initiate another animation while the current animation is playing. The buffered animation will play as soon as the current animation becomes cancelable or ends.
For general ideas about how the combat system is supposed to behave for the player, see the design document's combat section.
Combat Data Format
The x.lisp
file that contains the animation data is formatted using standard Lisp syntax. It is structured using property lists:
:source
:animations
name
The name as used by the game. This must correspond with the name of a tag in Aseprite and with the name used in the internal engine. Please see the animation sheet for the correct names.:loop-to
See above.:next
See above.
:frames
These properties mostly correspond 1:1 with the above descriptions. Vectors are denoted by lists of numbers.:damage
:stun-time
:flags
The flags are expressed as a bit array in the (left to right) order of cancelable, invincible, interruptable.:effect
The effect must correspond to the name of an effect as known by the game. Please see the ":velocity
:multiplier
:knockback
:hurtbox
Typically you will not want to edit this file manually and instead use the animation editor.
Exporting from Aseprite
When exporting from Aseprite to update animations, please choose "Export Sprite Sheet" with the following options:
Layout
Sheet Type: Packed
Constraints: None
Borders
Border padding: 0
Spacing: 1
Inner Padding: 0
Trim Cels: checked
Output
Output File: checked
Name:x.png
JSON Data: checked
Name:x.json
Type: Array
Filename:{tagframe} {tag}
You can also do this for most files in an automated fashion by just running the following from the Portacle REPL:
(kandria::compile-resources T T)